//
// Created by root on 6/27/17.
//

#include <stdlib.h>
#include <getopt.h>
#include <stdio.h>
#include <memory.h>
#include <limits.h>
#include "ds/cell.h"
#include "basic/operator.h"

#define IMPORT_STATEMENT  "(import "

struct globalArgs_t {
    char *filePath;
    char *basePath;
} globalArgs;

static Scheme *alloc_scheme() {
    return (Scheme *) malloc(sizeof(Scheme));
}

static void usage(const char *argv0) {
    fprintf(stderr, "Usage: %s [-f file]|[-p package]\n", argv0);
    exit(EXIT_FAILURE);
}

static void set_default_args() {
    globalArgs.filePath = NULL;
    globalArgs.basePath = NULL;
}

static void load_file(Scheme *sc, char *filePath);

//eval each line
static void eval_line(Scheme *sc, char *line, char *currentPath) {
    char *p;
    char *q;
    char *filePath[PATH_MAX];
    static int line_no = 1;
    if (strlen(line) != 0) {
        if (strncmp(IMPORT_STATEMENT, line, strlen(IMPORT_STATEMENT)) == 0) {
            p = strchr(line, '"');
            q = strchr(p + 1, '"');
            *q = '\0';
            strcpy(filePath, p + 1);
            if (!contains_queue_element(sc->loadedFiles, filePath, equals_string)) {
                load_file(sc, filePath);
                enqueue(sc->loadedFiles, filePath);
            }
        }

        Cell* result = eval(sc, line);
        printf("%s[%d]: %s\n", currentPath, line_no, cell2str(sc, sc->returnValue));
        line_no++;
    }
}

static String get_real_path(Scheme *sc) {
    String path = init_string();
    path = dyna_strcat(strdup(sc->basePath), sc->filePath);
    return path;
}

static void load_file(Scheme *sc, char *filePath) {
    if (filePath == NULL) {
        fprintf(stderr, "Load file error!\n");
        exit(EXIT_FAILURE);
    }

    FILE *fp = fopen(filePath, "rb");
    if (!fp) {
        perror(filePath);
        exit(EXIT_FAILURE);
    }
    int size = 1024;
    int pos;
    int c;
    char *buffer = (char *)malloc(size);

    do { // read all lines in file
        pos = 0;
        do { // read one line
            c = fgetc(fp);
            if (c != EOF) buffer[pos++] = (char) c;
            if (pos >= size - 1) { // increase buffer length - leave room for 0
                size *= 2;
                buffer = (char *) realloc(buffer, size);
            }
        } while (c != EOF && c != '\n');
        buffer[pos] = 0;
        // line is now in buffer
        eval_line(sc, buffer, filePath);
    } while (c != EOF);
    fclose(fp);
}

int main(int argc, char **argv) {
    set_default_args();

    int opt;
    while ((opt = getopt(argc, argv, "f:p:")) != -1) {
        switch (opt) {
            case 'f':
                globalArgs.filePath = optarg;
                break;
            case 'p':
                globalArgs.basePath = optarg;
                break;
            default:
                usage(argv[0]);
        }
    }
    Scheme *sc = alloc_scheme();
    if (!scheme_init(sc)) {
        fprintf(stderr, "Initialize scheme environment error!\n");
        exit(EXIT_FAILURE);
    }
    sc->basePath = globalArgs.basePath;
    sc->filePath = globalArgs.filePath;
    sc->loadedFiles = init_queue();

    load_file(sc, get_real_path(sc));

    return 0;
}

